package com.vf.cloud.rendering.api.service.impl;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.List;
import org.springframework.stereotype.Service;
import com.jfinal.kit.JsonKit;
import com.jfinal.kit.StrKit;
import com.vf.cloud.rendering.api.service.IRender;
import com.vf.cloud.rendering.common.constant.Cache;
import com.vf.cloud.rendering.common.factory.UEFactory;
import com.vf.cloud.rendering.common.util.FileUtil;
import com.vf.cloud.rendering.common.util.GpuUtil;
import com.vf.cloud.rendering.common.util.IpUtil;
import com.vf.cloud.rendering.common.util.R;
import com.vf.cloud.rendering.common.vo.Config;
import com.vf.cloud.rendering.common.vo.Dispatch;
import com.vf.cloud.rendering.common.vo.Gpu;
import com.vf.cloud.rendering.common.vo.Order;
import com.vf.cloud.rendering.common.vo.Relay;
import com.vf.cloud.rendering.common.vo.Renderer;
import com.vf.cloud.rendering.common.vo.Server;
import com.vf.cloud.rendering.common.vo.Signalling;
import com.vf.cloud.rendering.common.vo.Streamer;
import com.vf.cloud.rendering.common.vo.req.AnyInfoParam;
import com.vf.cloud.rendering.common.vo.req.StartUpParam;
import com.vf.cloud.rendering.server.dispatch.DispatchServer;
import com.vf.cloud.rendering.server.relay.RelayServer;
import com.vf.cloud.rendering.server.signalling.SignallingServer;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class RenderImpl implements IRender {

	@Override
	public R<Order> AnyOrder(StartUpParam param) {
		if (param == null) {
			return R.failed("参数必填.");
		}

		if (StrKit.isBlank(param.getSid()) || StrKit.isBlank(param.getEio()) | StrKit.isBlank(param.getExeFilePath())) {
			return R.failed("参数必填.");
		}

		File f = new File(param.getExeFilePath());
		if (!f.exists()) {
			return R.failed("应用不存在.");
		}

		int status = SignallingServer.getInstance().getStatus();
		if (2 != status) {
			if (-1 == status) {
				return R.failed("信令服务不可用，无法提供渲染服务.");
			} else if (1 == status) {
				return R.failed("信令服务正在启动中，无法提供渲染服务.");
			} else if (0 == status) {
				return R.failed("信令服务已停止，无法提供渲染服务.");
			} else if (3 == status) {
				return R.failed("信令服务停止中，无法提供渲染服务.");
			}
		}

		String cmd = String.format(
				"cmd /c start /b %s -ForceRes -RenderOFFscreen -AudioMixer -AllowPixelStreamingCommands "
						+ "ResX=%s ResY=%s -PixelStreamingURL=%s -GraphicsAdapter=%s -AppId=%s -key=%s -PixelStreamingHideCursor=true %s",
				param.getExeFilePath(), param.getWidth(), param.getHeight(), String.format("ws://%s:%s/%s",
						Cache.signalling.getInnerIp(), Cache.signalling.getInnerPort(), param.getEio()),
				param.getIndex(), param.getSid(), param.getEio(), param.getParameter());

		Order order = new Order();
		order.setUrl(String.format("ws://%s:%s?EIO=%s", Cache.signalling.getIp(), Cache.signalling.getPort(),
				param.getEio()));
		order.setAppId(param.getSid());
		order.setEIO(param.getEio());

		Streamer streamer=new Streamer();
		streamer.setSid(param.getSid());
		streamer.setEIO(param.getEio());
		streamer.setMac(Cache.local.getMac());
		streamer.setUuid(param.getUuid());
		streamer.setIndex(param.getIndex());
		streamer.setBlock(param.getBlock());
		
		Process process;
		try {
			Runtime runtime = Runtime.getRuntime();
			process = runtime.exec(cmd);
			new Thread(new ConsoleThread(new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8"))))
					.start();
			if (process.waitFor() == 0) {
				UEFactory.getInstance().addEIOStreamer(param.getEio(), streamer);
				return R.ok(order);
			}
			return R.failed();
		} catch (IOException | InterruptedException e) {
			e.printStackTrace();
			UEFactory.getInstance().removeEIOStreamer(param.getEio());
			return R.failed(e.getMessage());
		}
	}

	class ConsoleThread implements Runnable {
		private BufferedReader bfr = null;

		public ConsoleThread(BufferedReader bfr) {
			this.bfr = bfr;
		}

		@Override
		public void run() {
			String line = null;
			try {
				while ((line = bfr.readLine()) != null) {
					log.debug(line);
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	@Override
	public R<Renderer> AnyInfo(AnyInfoParam param) {
		Renderer renderer = new Renderer();
		renderer.setGpus(GpuUtil.getGPUs());
		return R.ok(renderer);
	}

	@Override
	public R<Dispatch> AnyDispatchInfo() {
		return R.ok(Cache.dispatch);
	}

	@Override
	public R<Dispatch> AnyDispatchUpdate(Dispatch param) {
		if (param == null) {
			return R.failed("参数无效.");
		}

		if (StrKit.isBlank(param.getIp()) || StrKit.isBlank(param.getPort()) || StrKit.isBlank(param.getSecretKey())) {
			return R.failed("参数无效.");
		}
		if (!IpUtil.isIp(param.getIp())) {
			return R.failed("无效IP.");
		}

		if (!IpUtil.isPort(param.getPort())) {
			return R.failed("无效端口.");
		}
		return R.ok(param);
	}

	@Override
	public R<List<Server>> AnyServerList() {
		Server signalling = new Server();
		signalling.setAuto("1");
		signalling.setName("信令服务");
		signalling.setType("signalling");
		if (Cache.signalling != null) {
			signalling.setStatus(SignallingServer.getInstance().getStatus() + "");
		} else {
			signalling.setStatus("-1");
		}

		Server relay = new Server();
		relay.setName("中继服务");
		relay.setType("relay");
		if (Cache.relay != null) {
			relay.setStatus(RelayServer.getInstance().getStatus() + "");
			relay.setAuto(Cache.relay.getEnabled());
		} else {
			relay.setStatus("-1");
		}

		Server dispatch = new Server();
		dispatch.setName("调度服务");
		dispatch.setType("dispatch");
		dispatch.setAuto("1");
		if (Cache.dispatch != null) {
			dispatch.setStatus(DispatchServer.getInstance().getStatus() + "");
		} else {
			dispatch.setStatus("-1");
		}

		List<Server> list = new LinkedList<Server>();
		list.add(signalling);
		list.add(relay);
		list.add(dispatch);

		return R.ok(list);
	}

	@Override
	public R<Signalling> AnySignallingInfo() {
		return R.ok(Cache.signalling);
	}

	@Override
	public R<Signalling> AnySignallingUpdate(Signalling param) {
		if (Cache.signalling == null) {
			Cache.signalling = new Signalling();
		}
		Cache.signalling.setIp(param.getIp());
		Cache.signalling.setPort(param.getPort());
		Cache.signalling.setInnerIp(param.getInnerIp());
		Cache.signalling.setInnerPort(param.getInnerPort());
		Cache.signalling.setSslEnable(param.getSslEnable());
		Cache.signalling.setSslKeyAbsolutePath(param.getSslKeyAbsolutePath());
		Cache.signalling.setSslCertAbsolutePath(param.getSslCertAbsolutePath());

		Config config = new Config();
		config.setLocal(Cache.local);
		config.setRelay(Cache.relay);
		config.setSignalling(Cache.signalling);
		config.setDispatch(Cache.dispatch);
		FileUtil.writeConfig(JsonKit.toJson(config));
		SignallingServer.getInstance().stop();
		return R.ok();

	}

	@Override
	public R<Relay> AnyRelayInfo() {
		return R.ok(Cache.relay);
	}

	@Override
	public R<Relay> AnyRelayUpdate(Relay param) {

		if (Cache.relay == null) {
			Cache.relay = new Relay();
		}
		Cache.relay.setIp(param.getIp());
		Cache.relay.setStunPort(param.getStunPort());
		Cache.relay.setTurnPort(param.getTurnPort());
		Cache.relay.setUsername(param.getUsername());
		Cache.relay.setPassword(param.getPassword());
		Cache.relay.setEnabled(param.getEnabled());

		Config config = new Config();
		config.setLocal(Cache.local);
		config.setRelay(Cache.relay);
		config.setSignalling(Cache.signalling);
		config.setDispatch(Cache.dispatch);
		FileUtil.writeConfig(JsonKit.toJson(config));
		RelayServer.getInstance().restart();
		return R.ok();
	}

	@Override
	public R<List<Gpu>> AnyGpuList() {
		return R.ok(GpuUtil.getGPUs());
	}

	@Override
	public R<Order> startUp(StartUpParam param) {
		return null;
	}

}
